package cc.autoapi.pucong.auto.flow.core.flow.execNode.task;


import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.ApiHandlerMsgDto;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.PropertiesVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.node.*;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.task.NodeExecResult;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exception.AutoDesignException;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.DataBus;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.publish.ProcessExecApiPublish;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.publish.ProcessExecNodePublish;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.DataTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.NodeTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.ParamPositionEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.RunStatusEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.utils.DataValueUtils;
import com.alibaba.fastjson2.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public abstract class AbstractTask implements InterfaceTask<Boolean> {

    private String instanceId;
    private String apiId;
    private ExecNode execNode;

    public final ExecNode getExecNode() {
        return execNode;
    }

    public final void setInstanceId(String instanceId) {
        this.instanceId = instanceId;
    }

    public final String getInstanceId() {
        return instanceId;
    }

    public String getApiId() {
        return apiId;
    }

    public void setApiId(String apiId) {
        this.apiId = apiId;
    }

    public final Map<String, String> getHeaderParamMap() {
        Map<String, String> headerParams = DataBus.getHeaderParamMap(instanceId);
        return headerParams;
    }

    public final Map<String, Object> getQueryParams() {
        Map<String, Object> queryParams = DataBus.getQueryParams(instanceId);
        return queryParams;
    }

    public final Map<String, Object> getBodyParamMap() {
        Map<String, Object> bodyParamMap = DataBus.getBodyParamMap(instanceId);
        return bodyParamMap;
    }


    public final void setExecNode(ExecNode execNode) {
        this.execNode = execNode;
    }

    @Override
    public final Boolean call() {
        handle();
        return true;
    }

    @Override
    public final void handle() {
        Boolean isIgnore = execNode.getIsIgnore();
        // 没有忽略执行
        if (isIgnore) {
            // 设置节点状态
            execNode.setNodeExecResultIgnore();
        } else {
            // 前置执行
            preHandle();
            // 执行
            run();
            // 后置执行
            afterHandle();
            // 设置节点状态
            execNode.setNodeExecResultOk();
        }
        // 执行下一节点
        publish2NextNode();
    }

    public final void checkAndSetParamsValue() {
        List<ExecParamIn> execNodeParamInList = execNode.getParamInList();
        for (ExecParamIn execParamIn : execNodeParamInList) {
            String paramCode = execParamIn.getParamCode();
            Boolean needValue = execParamIn.getNeedValue();
            DataTypeEnum dateType = execParamIn.getDateType();
            Object dateValue = null;
            if (NodeTypeEnum.API_CONFIG.getKey().equals(execNode.getNodeType())) {
                ParamPositionEnum paramPosition = execParamIn.getParamPosition();
                Map<String, Object> queryMap = DataBus.getQueryParams(instanceId);
                Map<String, Object> bodyMap = DataBus.getBodyParamMap(instanceId);
                Map<String, String> headerMap = DataBus.getHeaderParamMap(instanceId);
                switch (paramPosition) {
                    case QUERY:
                        dateValue = queryMap.get(paramCode);
                        break;
                    case BODY:
                        dateValue = bodyMap.get(paramCode);
                        break;
                    case HEADER:
                        dateValue = headerMap.get(paramCode);
                        break;
                    default:
                        throw new AutoDesignException("传入参数位置错误");
                }
            } else {
                String paramValue = execParamIn.getParamValue();
                if (StringUtils.isBlank(paramValue) && needValue) {
                    throw new AutoDesignException("节点参数配置错误，节点参数为必填项，请检查节点参数配置:" + paramCode);
                } else if (StringUtils.isBlank(paramValue) && !needValue) {
                    continue;
                }
                // 参数类型 0静态值 1前置接口返回值 2全局参数
                Integer paramDataType = execParamIn.getParamDataType();
                if (paramDataType == 1) {
                    String paramValueId = JSONArray.parseArray(paramValue).getString(1);
                    if (StringUtils.isBlank(paramValueId) && needValue) {
                        throw new AutoDesignException("节点参数配置错误，节点参数为必填项，请检查节点参数配置:" + paramCode);
                    }
                    // 是循环取值，跳过
                    if ("itemKey".equals(paramValueId) || "itemValue".equals(paramValueId)) {
                        continue;
                    }
                    // 获取值
                    List<TaskNodeParamOut> taskNodeParamsOutList = DataBus.getTaskNodeParamsOutList(apiId);
                    TaskNodeParamOut taskNodeParamOut = taskNodeParamsOutList.stream().filter(s -> s.getUuid().equals(paramValueId)).findFirst().orElse(null);
                    if (taskNodeParamOut != null) {
                        ExecNode execNodeById = getExecNodeById(taskNodeParamOut.getApiNodeId());
                        dateValue = execNodeById.getParamOutValue(taskNodeParamOut.getParamCode());
                    } else {
                        List<TaskNodeParamIn> taskNodeParamsInList = DataBus.getTaskNodeParamsInList(apiId);
                        TaskNodeParamIn taskNodeParamIn = taskNodeParamsInList.stream().filter(s -> s.getUuid().equals(paramValueId)).findFirst().orElse(null);
                        if (taskNodeParamIn != null) {
                            ExecNode execNodeById = getExecNodeById(taskNodeParamIn.getApiNodeId());
                            dateValue = execNodeById.getParamInValue(taskNodeParamIn.getParamCode());
                        }
                    }
                } else if (paramDataType == 0) {
                    dateValue = paramValue;
                } else if (paramDataType == 2) {
                    // 全局参数
                    String propertyCode = JSONArray.parseArray(paramValue).getString(1);
                    PropertiesVo property = DataBus.getProperty(propertyCode);
                    if (property == null) {
                        throw new AutoDesignException("节点参数配置错误，节点使用全局参数不存在，请检查节点参数配置:" + paramCode);
                    }
                    dateValue = property.getPropertiesValue();
                }
            }
            if (dateValue == null && needValue) {
                throw new AutoDesignException("节点参数配置错误，节点参数为必填项，请检查节点参数配置:" + paramCode);
            }
            if (dateValue != null && needValue && dateValue instanceof String && StringUtils.isBlank(dateValue.toString())) {
                throw new AutoDesignException("节点参数配置错误，节点参数为必填项，请检查节点参数配置:" + paramCode);
            }
            // 转换数据类型
            execParamIn.setDateValue(DataValueUtils.transDataType(paramCode, dateType, dateValue));
        }
    }

    public final ExecParamOut getParamOutInfo(String paramCode) {
        List<ExecParamOut> taskParamOutList = execNode.getParamOutList();
        Map<String, ExecParamOut> execParamOutMap = taskParamOutList.stream().collect(Collectors.toMap(ExecParamOut::getParamCode, s -> s));
        ExecParamOut execParamOut = execParamOutMap.get(paramCode);
        return execParamOut;
    }

    /**
     * 获取输入参数值
     *
     * @param paramCode
     * @return
     */
    public final ExecParamIn getParamInInfo(String paramCode) {
        List<ExecParamIn> taskParamInList = execNode.getParamInList();
        Map<String, ExecParamIn> execParamInHashMap = taskParamInList.stream().collect(Collectors.toMap(ExecParamIn::getParamCode, s -> s));
        ExecParamIn execParamIn = execParamInHashMap.get(paramCode);
        return execParamIn;
    }

    /**
     * 设置输出参数值
     *
     * @param paramCode
     * @param dataValue
     */
    public void setParamOutValue(String paramCode, Object dataValue) {
        List<ExecParamOut> taskParamOutList = execNode.getParamOutList();
        Map<String, ExecParamOut> execParamInHashMap = taskParamOutList.stream().collect(Collectors.toMap(ExecParamOut::getParamCode, s -> s));
        ExecParamOut execParamOut = execParamInHashMap.get(paramCode);
        execParamOut.setDateValue(dataValue);
    }

    private synchronized void publish2NextNode() {
        String nodeId = execNode.getUuid();
        Boolean isIgnore = execNode.getIsIgnore();
        // 查询下一节点
        List<TaskNode> nextNodeList = getNextNodeList(nodeId);
        if (!nextNodeList.isEmpty()) {
            ProcessExecNodePublish nodeExecPublish = DataBus.getNodeExecPublish();
            List<TaskNode> runNodeList = null;
            if (!isIgnore) {
                runNodeList = fitterNextPublishNode(nextNodeList);
            }
            if (runNodeList == null) {
                runNodeList = new ArrayList<>();
            }
            List<String> runNodeIdList = runNodeList.stream().map(TaskNode::getUuid).collect(Collectors.toList());
            for (TaskNode nextNode : nextNodeList) {
                String nextId = nextNode.getUuid();
                // 查找兄弟节点
                List<TaskNode> brotherNodeList = getParentNodeList(nextId);
                // 兄弟节点是否执行完成
                Boolean finish = checkBrotherNodeFinish(brotherNodeList);
                // 通知执行下一节点
                if (finish) {
                    // 通知执行
                    NodeHandlerMsgDto nodeHandlerMsgDto = new NodeHandlerMsgDto(apiId, instanceId, nextId, !runNodeIdList.contains(nextId));
                    nodeExecPublish.publicMessageByAsync(nodeHandlerMsgDto);
                }
            }
        }
        if (nextNodeList.isEmpty() && !isIgnore) {
            // 整个API都执行结束
            ProcessExecApiPublish apiPublish = DataBus.getApiPublish();
            apiPublish.publicMessageByAsync(ApiHandlerMsgDto.ok(apiId, instanceId));
        }
    }

    public ExecNode getExecNodeById(String nodeId) {
        List<ExecNode> execNodeList = DataBus.getExecNodeList(instanceId);
        ExecNode execNode1ById = execNodeList.stream().filter(node -> node.getUuid().equals(nodeId)).findFirst().orElse(null);
        return execNode1ById;
    }

    /**
     * 查询下一节点
     *
     * @param nodeId
     * @return
     */
    private List<TaskNode> getNextNodeList(String nodeId) {
        List<TaskNodeRelation> taskNodeRelationList = DataBus.getTaskNodeRelationList(apiId);
        List<String> nextNodeIdList = taskNodeRelationList.stream().filter(relation -> relation.getNodeFromId().equals(nodeId)).map(relation -> relation.getNodeToId()).collect(Collectors.toList());
        List<TaskNode> nextNodeList = new ArrayList<>();
        List<TaskNode> execNodeList = DataBus.getTaskNodeList(apiId);
        Map<String, TaskNode> execParamInHashMap = execNodeList.stream().collect(Collectors.toMap(TaskNode::getUuid, s -> s));
        for (String id : nextNodeIdList) {
            TaskNode execNode1 = execParamInHashMap.get(id);
            if (execNode1 != null) {
                nextNodeList.add(execNode1);
            }
        }
        return nextNodeList;
    }

    /**
     * 兄弟节点是否执行完成
     *
     * @param brotherNodeList
     * @return
     */
    private Boolean checkBrotherNodeFinish(List<TaskNode> brotherNodeList) {
        if (brotherNodeList.size() == 1) {
            return true;
        }
        for (TaskNode taskNode : brotherNodeList) {
            ExecNode execNode = getExecNodeById(taskNode.getUuid());
            if (execNode == null) {
                return false;
            }
            NodeExecResult nodeExecResult = execNode.getNodeExecResult();
            if (nodeExecResult == null) {
                return false;
            }
            if (nodeExecResult.getRunStatus() != RunStatusEnum.执行成功 && nodeExecResult.getRunStatus() != RunStatusEnum.已跳过) {
                return false;
            }
        }
        return true;
    }

    /**
     * 获取父级节点
     *
     * @param nodeId
     * @return
     */
    private List<TaskNode> getParentNodeList(String nodeId) {
        List<TaskNode> brotherNodeList = new ArrayList<>();
        List<TaskNode> execNodeList = DataBus.getTaskNodeList(apiId);
        List<TaskNodeRelation> taskNodeRelationList = DataBus.getTaskNodeRelationList(apiId);
        List<String> execNodeIds = taskNodeRelationList.stream().filter(relation -> relation.getNodeToId().equals(nodeId)).map(relation -> relation.getNodeFromId()).collect(Collectors.toList());
        for (String id : execNodeIds) {
            TaskNode brotherNode = execNodeList.stream().filter(node -> node.getUuid().equals(id)).findFirst().get();
            brotherNodeList.add(brotherNode);
        }
        return brotherNodeList;
    }

    public abstract void run();

    public abstract List<NodeParamIn> getPropertyParamsIns();

    public abstract List<NodeParamOut> getPropertyParamsOuts();

    public abstract String getTaskName();

    public String getTaskDesc() {
        return "";
    }

    /**
     * 节点图标
     *
     * @return
     */
    public String getTaskSvgIcon() {
        return "<svg t=\"1727020420280\" class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"13165\" width=\"32\" height=\"32\"><path d=\"M853.333333 896H320c-23.466667 0-42.666667-19.2-42.666667-42.666667v-128c-29.866667 2.133333-61.866667-4.266667-89.6-21.333333-38.4-25.6-61.866667-68.266667-61.866666-115.2s23.466667-91.733333 64-117.333333c25.6-17.066667 57.6-25.6 87.466666-21.333334v-128c0-23.466667 19.2-42.666667 42.666667-42.666666h128c-4.266667-32 4.266667-64 21.333333-91.733334 25.6-38.4 68.266667-61.866667 115.2-61.866666s91.733333 23.466667 117.333334 64c17.066667 27.733333 25.6 57.6 21.333333 89.6h128c23.466667 0 42.666667 19.2 42.666667 42.666666v196.266667c0 29.866667-32 51.2-59.733334 38.4l-36.266666-14.933333c-17.066667-6.4-42.666667-6.4-55.466667 4.266666-14.933333 10.666667-23.466667 25.6-23.466667 44.8 0 17.066667 8.533333 32 21.333334 42.666667s38.4 10.666667 53.333333 4.266667l38.4-17.066667c27.733333-12.8 59.733333 8.533333 59.733333 38.4V853.333333c4.266667 23.466667-14.933333 42.666667-38.4 42.666667z m-490.666666-85.333333h448v-89.6c-38.4 10.666667-78.933333 4.266667-113.066667-17.066667-38.4-25.6-61.866667-68.266667-61.866667-115.2s23.466667-91.733333 64-117.333333c34.133333-21.333333 72.533333-27.733333 110.933334-19.2V362.666667h-142.933334c-29.866667 0-51.2-32-38.4-59.733334l6.4-14.933333c8.533333-19.2 8.533333-40.533333-4.266666-55.466667-10.666667-14.933333-25.6-23.466667-44.8-23.466666-17.066667 0-32 8.533333-42.666667 21.333333s-10.666667 38.4-4.266667 53.333333l8.533334 17.066667c12.8 27.733333-8.533333 59.733333-38.4 59.733333H362.666667v142.933334c0 29.866667-32 51.2-59.733334 38.4l-12.8-4.266667c-17.066667-6.4-42.666667-6.4-55.466666 4.266667-14.933333 10.666667-23.466667 25.6-23.466667 44.8 0 17.066667 8.533333 32 21.333333 42.666666s38.4 10.666667 53.333334 4.266667l14.933333-6.4c27.733333-12.8 59.733333 8.533333 59.733333 38.4V810.666667z\" fill=\"#297AFF\" p-id=\"13166\"></path></svg>";
    }

    /**
     * 节点分类id
     *
     * @return
     */
    public String getTaskClassId() {
        return "1";
    }

    public Boolean getTaskParamInAuto() {
        return false;
    }

    public NodeParamIn getPropertyParamsIn(String paramCode) {
        List<NodeParamIn> paramsIns = getPropertyParamsIns();
        return paramsIns.stream().filter(item -> item.getParamCode().equals(paramCode)).findFirst().orElse(null);
    }

    public NodeParamOut getPropertyParamsOut(String paramCode) {
        List<NodeParamOut> paramsOuts = getPropertyParamsOuts();
        return paramsOuts.stream().filter(item -> item.getParamCode().equals(paramCode)).findFirst().orElse(null);
    }
}
